package com.infore.authorityManagement.select.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.SetJoin;
import javax.validation.constraints.NotNull;

import org.springframework.data.domain.PageImpl;
import org.springframework.util.StringUtils;

import com.infore.authorityManagement.select.chain.ConditionFilterChain;
import com.infore.authorityManagement.select.dto.QueryParamDto;
import com.infore.authorityManagement.select.enums.OperatorEnum;
import com.infore.authorityManagement.select.filter.condition.BetweenFilter;
import com.infore.authorityManagement.select.filter.condition.EqFilter;
import com.infore.authorityManagement.select.filter.condition.GeFilter;
import com.infore.authorityManagement.select.filter.condition.GtFilter;
import com.infore.authorityManagement.select.filter.condition.InFilter;
import com.infore.authorityManagement.select.filter.condition.IsNotNullFilter;
import com.infore.authorityManagement.select.filter.condition.IsNullFilter;
import com.infore.authorityManagement.select.filter.condition.LeFilter;
import com.infore.authorityManagement.select.filter.condition.LikeFilter;
import com.infore.authorityManagement.select.filter.condition.LtFilter;
import com.infore.authorityManagement.select.filter.condition.NotEqFilter;
import com.infore.authorityManagement.select.filter.condition.NotInFilter;
import com.infore.authorityManagement.select.filter.condition.NotLikeFilter;
import com.infore.authorityManagement.util.Page;

public class QueryUtil {
	
	
	/**
	 * 通过包装类Root获取属性路径
	 * @param nature
	 * @param root
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Path getPropertyPath(String propertyName, Root<?> root) {
		// 注意仅限一层关联，如protocolType.name
		if (propertyName.contains(".")) {
			String[] fieldNames = propertyName.split("\\.");
			Path path = root.get(fieldNames[0]);
            Class clazz = path.getJavaType();
            if (clazz.equals(Set.class)) {
                SetJoin setJoin = root.joinSet(fieldNames[0]);
                path = setJoin.get(fieldNames[1]);
            } else if (clazz.equals(List.class)) {
                ListJoin listJoin = root.joinList(fieldNames[0]);
                path = listJoin.get(fieldNames[1]);
            } else if (clazz.equals(Map.class)) {
                MapJoin mapJoin = root.joinMap(fieldNames[0]);
                path = mapJoin.get(fieldNames[1]);
            } else {
            	// 是many to one时
//            	path = path.get(fieldNames[1]);
            	path = null;
            	for (String string : fieldNames) {
    				if (path == null) {
    					path = root.get(string);
    				} else {
    					path = path.get(string);
    				}
    			}
            }
			return path;
		} else {
			return root.get(propertyName);
		}
	}
	
	public static ConditionFilterChain getConditionFilterChain() {
		ConditionFilterChain chain = new ConditionFilterChain();
		chain.addFilter(new EqFilter());
		chain.addFilter(new NotEqFilter());
		chain.addFilter(new GtFilter());
		chain.addFilter(new LtFilter());
		chain.addFilter(new GeFilter());
		chain.addFilter(new LeFilter());
		chain.addFilter(new IsNullFilter());
		chain.addFilter(new IsNotNullFilter());
		chain.addFilter(new LikeFilter());
		chain.addFilter(new NotLikeFilter());
		chain.addFilter(new NotInFilter());
		chain.addFilter(new InFilter());
		chain.addFilter(new BetweenFilter());
		return chain;
	}
	
	public static Predicate getPredicate(int i,List<String> qssembles,List<Predicate> predicates,
			List<Predicate> newPredicates,CriteriaBuilder criteriaBuilder) {
		Predicate predicate = null;
		String rule = qssembles.get(i).replaceAll(" ", "");
		String[] rules = null;
		Predicate predicate_left = null;
		Predicate predicate_right = null;
		if(rule.contains(OperatorEnum.OR.getOperator())) {
			rules = rule.split(OperatorEnum.OR.getOperator());
			if(rules[0].contains("_")) {
				predicate_left = newPredicates.get(Integer.valueOf(rules[0].substring(1)));
			}else {
				predicate_left = predicates.get(Integer.valueOf(rules[0]));
			}
			if(rules[1].contains("_")) {
				predicate_right = newPredicates.get(Integer.valueOf(rules[1].substring(1)));
			}else {
				predicate_right = predicates.get(Integer.valueOf(rules[1]));
			}
			predicate = criteriaBuilder.or(predicate_left,predicate_right);
			newPredicates.add(predicate);
		}else if (rule.contains(OperatorEnum.AND.getOperator())) {
			rules = rule.split(OperatorEnum.AND.getOperator());
			if(rules[0].contains("_")) {
				predicate_left = newPredicates.get(Integer.valueOf(rules[0].substring(1)));
			}else {
				predicate_left = predicates.get(Integer.valueOf(rules[0]));
			}
			if(rules[1].contains("_")) {
				predicate_right = newPredicates.get(Integer.valueOf(rules[1].substring(1)));
			}else {
				predicate_right = predicates.get(Integer.valueOf(rules[1]));
			}
			predicate = criteriaBuilder.and(predicate_left,predicate_right);
			newPredicates.add(predicate);
		} else if (rule.length() == 1) {
			predicate = predicates.get(Integer.valueOf(0));
		} else {
			throw new RuntimeException("组装查询条件不能为空！");
		}
		return predicate;
	}
	
	/**
	 * 组装select
	 * @param select
	 * @param criteriaBuilder
	 * @param query
	 * @param root
	 */
	public static void assembleSelect(String select,CriteriaBuilder criteriaBuilder,CriteriaQuery<?> query,Root<?> root) {
		List<Selection<?>> selections = QueryUtil.getSelections(Arrays.asList(select.split(",")), criteriaBuilder, root);
		query.multiselect(selections);
	}
	
	/**
	 * 组装group
	 * @param query
	 * @param root
	 * @param group
	 */
	@SuppressWarnings("rawtypes")
	public static void assembleGroup(CriteriaQuery<?> query,Root<?> root,String group) {
		if(!StringUtils.isEmpty(group)) {
			List<String> groups = Arrays.asList(group.split(","));
			for (String gro : groups) {
				if (gro.contains(".")) { // 判断是否存在多属性(protocolType.code)
					String[] fields = gro.split("\\.");
					Path path = null;
					for (String field : fields) {
						if (path == null) {
							path = root.get(field);
						} else {
							path = path.get(field);
						}
					}
					query.groupBy(path);
				} else {
					query.groupBy(root.get(gro));
				}
			}
		}
	}
	
	/**
	 * 组装order
	 * @param criteriaBuilder
	 * @param query
	 * @param root
	 * @param orders
	 */
	@SuppressWarnings("rawtypes")
	public static void assembleOrder(CriteriaBuilder criteriaBuilder,CriteriaQuery<?> query,Root<?> root,List<String> orders) {
		if(orders!=null&&orders.size()>0) {
			List<Order> orderList = new ArrayList<>();
			for(String order:orders) {
				String[] orderArr = order.split(",");
				if (orderArr[0].contains(".")) { // 判断属性值是否为副表的，比如protocolType.code
					String[] fieldNames = orderArr[0].split("\\.");
					Path path = null;
					for (String string : fieldNames) {
						if (path == null) {
							path = root.get(string);
						} else {
							path = path.get(string);
						}
					}
					if (orderArr[1].equalsIgnoreCase("asc")) {
						orderList.add(criteriaBuilder.asc(path));
					}else if (orderArr[1].equalsIgnoreCase("desc")) {
						orderList.add(criteriaBuilder.desc(path));
					}
				} else {
					if (orderArr[1].equalsIgnoreCase("asc")) {
						orderList.add(criteriaBuilder.asc(root.get(orderArr[0])));
					}else if (orderArr[1].equalsIgnoreCase("desc")) {
						orderList.add(criteriaBuilder.desc(root.get(orderArr[0])));
					}
				}
			}
			if(orderList!=null&&orderList.size()>0) {
				query.orderBy(orderList);
			}
			
		}
	}
	
	/**
	 * 获取Selection集合
	 * @param selects
	 * @param criteriaBuilder
	 * @param root
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<Selection<?>> getSelections(List<String> selects, CriteriaBuilder criteriaBuilder, Root<?> root) {
		List<Selection<?>> selections = new ArrayList<>(); // 创建查询指定字段集合
		for (String select : selects) {
			if (select.contains("count_")) { //统计个数
				select = select.replace("count_", "");
				selections.add(criteriaBuilder.count(QueryUtil.getPropertyPath(select, root)));
			} else if (select.contains("max_")) { //最大
				select = select.replace("max_", "");
				selections.add(criteriaBuilder.max(QueryUtil.getPropertyPath(select, root)));
			} else if (select.contains("min_")) { //最小
				select = select.replace("min_", "");
				selections.add(criteriaBuilder.min(QueryUtil.getPropertyPath(select, root)));
			} else if (select.contains("sum_")) { //总和
				select = select.replace("sum_", "");
				selections.add(criteriaBuilder.sum(QueryUtil.getPropertyPath(select, root)));
			} else {
				selections.add(QueryUtil.getPropertyPath(select, root));
			}
		}
		return selections;
	}
	
	/**
	 * 分页查询
	 */
	public static List<?> getPage(int totalRecords,List<?> result,TypedQuery<?> typedQuery,QueryParamDto queryParamDto,Page<?> page) {
		PageObject pageObject = queryParamDto.getPageObject();
		if(pageObject!=null) {
			// 分页
			org.springframework.data.domain.Page<?> pageResult = QueryUtil.getPageResult(typedQuery, pageObject, totalRecords);
			// 获取分页后的数据集
			result = pageResult.getContent();
			page.setPageNo(pageObject.getPageNumber());
			page.setPageSize(pageObject.getPageSize());
			page.setTotalRecords(totalRecords);
			page.setTotalPages(pageResult.getTotalPages());
		}
		return result;
	}
	
	/**
	 * 获取分页结果集
	 * @param typedQuery
	 * @param pageObject
	 * @param totalRecords
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static org.springframework.data.domain.Page<?> getPageResult(TypedQuery<?> typedQuery, PageObject pageObject, int totalRecords) {
		int pageNumber = pageObject.getPageNumber() - 1;
		int pageSize = pageObject.getPageSize();
		typedQuery.setFirstResult(pageNumber * pageSize);
		typedQuery.setMaxResults(pageSize);
		org.springframework.data.domain.Page pageResult = new PageImpl(typedQuery.getResultList(), pageObject.getPageRequest(),
				totalRecords);
		return pageResult;
	}
	
	
	/**
	 * 创建查询条件
	 * 
	 * @param queryParamDto
	 * @param criteriaBuilder
	 * @param root
	 * @return
	 */
	public static List<Predicate> createQueryCondition(@NotNull QueryParamDto queryParamDto,
			@NotNull CriteriaBuilder criteriaBuilder, @NotNull Root<?> root) {
		List<Predicate> predicates = new ArrayList<>();
		if (queryParamDto != null) {
			// 取出查询条件
			QueryCondition condition = queryParamDto.getQueryCondition();
			if (condition != null) {
				// 获取查询条件列名,操作符,匹配内容
				List<String> propertys = condition.getProperty();
				List<String> operators = condition.getOperator();
				List<String> contents = condition.getContent();
				if (!StringUtils.isEmpty(propertys) && !StringUtils.isEmpty(operators)
						&& !StringUtils.isEmpty(contents)) {
					if (propertys.size() == operators.size() && operators.size() == contents.size()) {
						// 获取过滤链，调用里面注册的过滤器
						ConditionFilterChain filterChain = QueryUtil.getConditionFilterChain();
						for (int i = 0; i < propertys.size(); i++) {
							// 获取一条完整的查询条件，包括列名，操作符，匹配内容
							String property = propertys.get(i);
							String operator = operators.get(i);
							String content = contents.get(i);
							filterChain.doFilter(predicates, criteriaBuilder, root, property, operator, content,
									filterChain, false);
						}
					} else {
						throw new RuntimeException("查询条件列名，操作符，匹配内容必须一一匹配！");
					}
				} else {
					throw new RuntimeException("查询条件列名，操作符，匹配内容不允许为空！");
				}
			}
		}
		return predicates;
	}
	
	/**
	 * 组装查询条件
	 * 
	 * @param queryParamDto
	 * @param criteriaBuilder
	 * @param predicates
	 */
	public static Predicate assembleCondition(@NotNull QueryParamDto queryParamDto, @NotNull CriteriaBuilder criteriaBuilder,
			@NotNull List<Predicate> predicates) {
		Predicate predicate = null;
		if (queryParamDto != null) {
			// 取出组装查询条件
			List<String> qssembles = queryParamDto.getAssembleCondition();
			List<Predicate> newPredicates = new ArrayList<>();
			if (qssembles != null) {
				for (int i = 0; i < qssembles.size(); i++) {
					predicate = QueryUtil.getPredicate(i, qssembles, predicates, newPredicates, criteriaBuilder);
				}
			}
		}
		return predicate;
	}

	/**
	 * 组装SQL
	 * 
	 * @param queryParamDto
	 * @param criteriaBuilder
	 * @param predicates
	 */
	public static void assembleSql(@NotNull QueryParamDto queryParamDto, @NotNull CriteriaBuilder criteriaBuilder,
			@NotNull CriteriaQuery<?> query, @NotNull Root<?> root, @NotNull Predicate predicate) {
		if (queryParamDto != null) {
			// 取出组装sql
			AssembleSql qssembleSql = queryParamDto.getAssembleSql();
			if (qssembleSql != null) {
				// 获取查询条件列名,操作符,匹配内容
				String select = qssembleSql.getSelect();
				String group = qssembleSql.getGroup();
				List<String> order = qssembleSql.getOrder();
				if (!StringUtils.isEmpty(select)) {
					QueryUtil.assembleSelect(select, criteriaBuilder, query, root);
				}
				if (!StringUtils.isEmpty(group)) {
					QueryUtil.assembleGroup(query, root, group);
				}
				if (order != null && order.size() > 0) {
					QueryUtil.assembleOrder(criteriaBuilder, query, root, order);
				}
			}
			if (predicate != null) { // 如果predicate不为空,才添加查询条件
				query.where(predicate);
			}
		}
	}

}